/*****************************************************************
 *   Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 ****************************************************************/
package org.epiphron.wam;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Operations registry
 */
public class Operations {
	static final Map<String, IOperationDescription> operations = 
			new HashMap<String, IOperationDescription>();
	static final Logger logger = LoggerFactory.getLogger(Operations.class);
	
	static {
		put(new OperationDescription("AddCookieOperation"), HttpServletResponse.class, "addCookie", Cookie.class);
		
		OperationDescription headerOp = new OperationDescription("AddResponseHeaderOperation");
		put(headerOp, HttpServletResponse.class, "addHeader", String.class, String.class);
		put(headerOp, HttpServletResponse.class, "addDateHeader", String.class, Date.class);
		put(headerOp, HttpServletResponse.class, "addIntHeader", String.class, Integer.TYPE);
		put(headerOp, HttpServletResponse.class, "setHeader", String.class, String.class);
		put(headerOp, HttpServletResponse.class, "setDateHeader", String.class, Date.class);
		put(headerOp, HttpServletResponse.class, "setIntHeader", String.class, Integer.TYPE);
		
		OperationDescription readHeaderOp = new OperationDescription("ReadHeaderOperation");
		put(readHeaderOp, HttpServletRequest.class, "getHeader", String.class);
		put(readHeaderOp, HttpServletRequest.class, "getHeaders", String.class);
		
		OperationDescription readParameterOp = new OperationDescription("ReadParameterOperation");
		put(readParameterOp, HttpServletRequest.class, "getParameter", String.class);
		put(readParameterOp, HttpServletRequest.class, "getParameterValues", String.class);
		
		OperationDescription redirectOp = new OperationDescription("RedirectOperation");
		put(redirectOp, HttpServletResponse.class, "sendRedirect", String.class);
		
		put(new OperationDescription("SessionInvalidateOperation", false), HttpSession.class, "invalidate");
		
		put(new OperationDescription("SetContentTypeOperation"), HttpServletResponse.class, "setContentType", String.class);
		
		OperationDescription responseCodeOp = new OperationDescription("SetResponseCodeOperation");
		put(responseCodeOp, HttpServletResponse.class, "sendError", Integer.TYPE);
		put(responseCodeOp, HttpServletResponse.class, "sendError", Integer.TYPE, String.class);
		put(responseCodeOp, HttpServletResponse.class, "setStatus", Integer.TYPE);
		put(responseCodeOp, HttpServletResponse.class, "setStatus", Integer.TYPE, String.class);
	}
	
	private static void put(IOperationDescription operation, Class<?> clazz, String methodName, Class<?>... types) {
		try {
			//Method method = clazz.getMethod(methodName, types);
			operations.put(getId(clazz, methodName, types), operation);
		}
		catch (Exception ex) {
			logger.warn("Cannot initialize operations", ex);
		}
	}

	private static String getId(Class<?> clazz, String methodName, Class<?>... types) {
		return clazz.getName() + "|" + methodName;
	}
	
	private static IOperationDescription find(Class<?> clazz, String methodName) {
		if (clazz == null || clazz == Object.class) {
			return null;
		}
		
		String id = getId(clazz, methodName);
		if (operations.containsKey(id)) {
			return operations.get(id);
		}
		
//		IOperationDescription op = find(clazz.getSuperclass(), methodName);
//		if (op != null) {
//			return op;
//		}
//		
//		for (Class<?> iface : clazz.getInterfaces()) {
//			op = find(iface, methodName);
//			if (op != null) {
//				return op;
//			}
//		}
		return null;
	}
	
	/**
	 * Looks for given method signature in operations registry
	 */
	static IOperationDescription find(String className, String methodName) {
		IOperationDescription op;
		try {
			op = find(Class.forName(className), methodName);
		} catch (ClassNotFoundException e) {
			logger.warn("Class not found: " + className + ", skipping instruction");
			return null;
		} catch (NoClassDefFoundError e) {
			logger.warn("Class not found: " + className + ", skipping instruction");
			return null;
		}
		
		return op;
	}
}
